
package ProOF.apl.methods.GA.population;

import ProOF.apl.problems.real.RealCodification;
import ProOF.apl.problems.real.RealProblem;
import ProOF.com.LinkerNodes;
import ProOF.com.LinkerParameters;
import ProOF.com.LinkerResults;
import ProOF.com.LinkerValidations;
import ProOF.com.language.Node;
import ProOF.gen.best.nEvaluations;
import ProOF.gen.operator.oCrossover;
import ProOF.gen.operator.oInitializer;
import ProOF.gen.operator.oMutation;
import ProOF.gen.stopping.aStop;
import ProOF.opt.abst.problem.meta.Problem;
import ProOF.opt.abst.problem.meta.Solution;
import ProOF.opt.abst.problem.meta.codification.Codification;
import ProOF.opt.abst.problem.meta.objective.SingleObjective;
import java.util.ArrayList;

/**
 *
 * @author Hossomi
 */
public abstract class aPopulation extends Node {

	protected Problem		problem;
	protected oInitializer	initializer;
	protected oCrossover	crossover[];
	protected oMutation		mutation[];
	protected double		crossRate;
	protected double		mutRate;
	
    protected int                   size;
    protected ArrayList<Solution>   individuals;
    protected Solution              best = null;
    protected Solution              worst = null;
    protected aStop                 stop;
    protected nEvaluations          eval;
    
    protected OperatorSet   opSet;
	
	public abstract void evolve() throws Exception;
    public abstract Solution[] select() throws Exception;
	
	@Override
	public String description() {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public void services(LinkerNodes link) throws Exception {
        problem = link.need( Problem.class, problem );
        stop = link.need( aStop.class, stop );
        eval = link.need( nEvaluations.class, eval );
		initializer = link.need( oInitializer.class, initializer);
		crossover = link.needs( oCrossover.class, new oCrossover[1]);
		mutation = link.needs( oMutation.class, new oMutation[1]);
	}

	@Override
	public void parameters(LinkerParameters link) throws Exception {
        size = link.Int( "Population size", 100, 1, 1000 );
		crossRate = link.Dbl( "Crossover Rate", 0.8, 0, 10 );
		mutRate = link.Dbl( "Mutation Rate", 0.7, 0, 1 );
	}

	@Override
	public void load() throws Exception {
		
	}

	@Override
	public void start() throws Exception {
		opSet = new OperatorSet(problem, crossover, mutation);
        individuals = new ArrayList<Solution>(size);
	}
	
    public void initialize() throws Exception {
        Solution sol;
		
        clear();
        
        System.out.println("Initializing population...");
		for (int i = 0; i < size; i++) {
            sol = problem.NewSolution();
			initializer.initialize(problem, sol.codif());
			problem.evaluate(sol);
            insert(sol);
		}
	}
    
    public void reinitialize() throws Exception {
        Solution sol = best;
		
        clear();
        insert(sol);
        
        System.out.println("Reinitializing population...");
		for (int i = 1; i < size; i++) {
            sol = problem.NewSolution();
			initializer.initialize(problem, sol.codif());
			problem.evaluate(sol);
            insert(sol);
		}
	}
 
    public boolean insert(Solution sol) throws Exception {
        if (individuals.size() < size) {
            individuals.add(sol);
            update(sol);
            return true;
        }
        else if (sol.LE(worst)) {
            individuals.remove(worst);
            individuals.add(sol);
            worst = null;
            update();
            return true;
        }
        
        return false;
    }
    
    public void remove( Solution sol ) {
        individuals.remove(sol);
        if (sol == best || sol == worst)
            update();
    }
    
    public void clear() {
        individuals.clear();
        best = null;
        worst = null;
    }
    
    public void update( Solution sol ) {
        if (best == null || sol.LE(best))
            best = sol;
        
        if (worst == null || sol.GE(worst))
            worst = sol;
    }
    
    public void update() {
        for (Solution sol: individuals) {
            if (best == null || sol.LE(best))
                best = sol;
            
            if (worst == null || sol.GE(worst))
                worst = sol;
        }
    }
    
	public void setProblem( Problem problem ) {
		this.problem = problem;
	}
    
    public Solution getBest() {
        return best;
    }
	
    public double getFitness( Solution sol ) {
        return ((SingleObjective) sol.obj()).abs_value();
    }
    
    public int getSize() {
        return size;
    }
    
    public double getMutationRate() {
        return mutRate;
    }
    
    public ArrayList<Solution> getIndividuals() {
        return individuals;
    }
    
	@Override
	public boolean validation(LinkerValidations link) throws Exception {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public void results(LinkerResults link) throws Exception {
		opSet.results(link);
	}
	
    protected class OperatorSet {
        private Problem problem;
        private oCrossover crossover[];
        private oMutation mutation[];
        
        public OperatorSet( Problem problem, oCrossover crossover[], oMutation mutation[] ) {
            this.problem = problem;
            this.crossover = crossover;
            this.mutation = mutation;
        }
        
        public void results( LinkerResults link ) throws Exception {
            
        }
        
        public Solution crossover( Solution ind1, Solution ind2 ) throws Exception {
            int i = problem.rmd.nextInt(0, crossover.length - 1);
            return problem.NewSolution(crossover[i].crossover( problem, ind1.codif(), ind2.codif() ));
        }
        
        public void mutation( Solution ind ) throws Exception {
            int i = problem.rmd.nextInt(0, mutation.length - 1);
            mutation[i].mutation(problem, ind.codif() );
        }
    }
}
